#include "Synch.h"
#include "Enum.h"

#undef UNICODE
#include <WS2tcpip.h>
#include <stdlib.h>
#include <stdio.h>

#pragma comment (lib, "Ws2_32.lib")

Synch::Synch()
{
	numClients = 0;
	NonBlocking = false;
    MainSocket = INVALID_SOCKET;
    result = NULL;
    ptr = NULL;
}

Synch::~Synch()
{
	if(MainSocket != INVALID_SOCKET)
		End();
}

void Synch::Start(int a_type)
{
	type = a_type;
    
    MainSocket = INVALID_SOCKET;
    result = NULL;
    ptr = NULL;
	// Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if(iResult != 0) {	printf("WSAStartup failed with error: %d\n", iResult);	return;}

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

	switch(type)
	{
	case SERVER:	ServerInit();	break;
	case CLIENT:	ClientInit();	break;
	};
}

/**server*/
InputStruct Synch::Synchronize(LostVector<DrawStruct>* draw)
{
	DrLiMe.setData(draw);
	if(!clientSocket || clientSocket == INVALID_SOCKET)
		CheckForNewClient();
	if(clientSocket != INVALID_SOCKET)
	{
		ServerSend();
		if(ServerRecieve())
			return InMes.getData();
	}
	InputStruct blank;
	return blank;
}

/**client*/
DrawListMessage* Synch::Synchronize(InputStruct input)
{
	InMes.setData(input);
	ClientSend();
	if(ClientRecieve())
	{
		return &DrLiMe;
	}
	return 0;
}

void Synch::End()
{
    closesocket(MainSocket);
    WSACleanup();
	MainSocket = INVALID_SOCKET;
}

void Synch::ServerInit()
{
    hints.ai_family = AF_INET;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if( iResult != 0 ) {	printf("getaddrinfo failed with error: %d\n", iResult);	WSACleanup();	return;}

    // Create a SOCKET for connecting to server
    MainSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if(MainSocket == INVALID_SOCKET) {	printf("socket failed with error: %ld\n", WSAGetLastError());	freeaddrinfo(result);	WSACleanup();	return;}

	//Make Blocking for connect
	NonBlocking = false;
	iResult = ioctlsocket(MainSocket, FIONBIO, &NonBlocking);
	if (iResult != NO_ERROR)	printf("ioctlsocket failed with error: %ld\n", iResult);


    // Setup the TCP listening socket
    iResult = bind(MainSocket, result->ai_addr, (int)result->ai_addrlen);
    if(iResult == SOCKET_ERROR) {	printf("bind failed with error: %d\n", WSAGetLastError());	freeaddrinfo(result);	closesocket(MainSocket);	WSACleanup();	return;}

    freeaddrinfo(result);

    iResult = listen(MainSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {	printf("listen failed with error: %d\n", WSAGetLastError());	closesocket(MainSocket);	WSACleanup();	return;}
}

void Synch::ClientInit()
{
    hints.ai_family = AF_UNSPEC;
	if(!ClientConnect())
	{
		printf("fail\n");
	}
}

bool Synch::ClientConnect()
{
	// Resolve the server address and port
	iResult = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
    if(iResult != 0) {	printf("getaddrinfo failed with error: %d\n", iResult);	WSACleanup();	return false;}

	// Attempt to connect to an address until one succeeds
	for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) 
	{
		// Create a SOCKET for connecting to server
		MainSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
		if (MainSocket == INVALID_SOCKET) {	printf("socket failed with error: %ld\n", WSAGetLastError());	WSACleanup();	return false;}

		//Make Blocking for connect
		NonBlocking = false;
		iResult = ioctlsocket(MainSocket, FIONBIO, &NonBlocking);
		if (iResult != NO_ERROR)	printf("ioctlsocket failed with error: %ld\n", iResult);

		// Connect to server.
		iResult = connect( MainSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
		if (iResult == SOCKET_ERROR) {	closesocket(MainSocket);	MainSocket = INVALID_SOCKET;	continue;}
		break;
	}

    freeaddrinfo(result);
    if (MainSocket == INVALID_SOCKET) {	printf("Unable to connect to server!\n");	WSACleanup();	return false;}

	////Make non-Blocking
	//NonBlocking = true;
	//iResult = ioctlsocket(MainSocket, FIONBIO, &NonBlocking);
	//if (iResult != NO_ERROR)	printf("ioctlsocket failed with error: %ld\n", iResult);

	return true;
}

void Synch::CheckForNewClient()
{
	clientSocket = accept(MainSocket, NULL, NULL);
	if(clientSocket != INVALID_SOCKET)
	{
		////Make non-Blocking
		//NonBlocking = true;
		//iResult = ioctlsocket(MainSocket, FIONBIO, &NonBlocking);
		//if (iResult != NO_ERROR)	printf("ioctlsocket failed with error: %ld\n", iResult);
	}
}

void Synch::ServerSend()
{
	clearBuffer(sendbuf);
	DrLiMe.pack(sendbuf);
	iSendResult = send(clientSocket, sendbuf, sizeof(sendbuf), 0);
	if (iSendResult == SOCKET_ERROR) {	printf("send failed with error: %d\n", WSAGetLastError());	closesocket(clientSocket);	WSACleanup();	return;}
}

void Synch::ClientSend()
{
	clearBuffer(sendbuf);
	InMes.pack(sendbuf);
	iResult = send( MainSocket, sendbuf, sizeof(sendbuf), 0 );
	if (iResult == SOCKET_ERROR) {	printf("send failed with error: %d\n", WSAGetLastError());	closesocket(MainSocket);	WSACleanup();	return;}
}

bool Synch::ServerRecieve()
{
	clearBuffer(recvbuf);
	iResult = recv(clientSocket, recvbuf, sizeof(recvbuf), 0);
	if(iResult > 0)
	{
		InMes.unPack(recvbuf);
		return true;
	}
	return false;
}

bool Synch::ClientRecieve()
{
	clearBuffer(recvbuf);
	iResult = recv(MainSocket, recvbuf, sizeof(recvbuf), 0);
	if(iResult < 0)	{	printf("recv failed with error: %d\n", WSAGetLastError());	closesocket(MainSocket);	WSACleanup();	return false;}
	DrLiMe.unPack(recvbuf);
	return true;
}

void Synch::clearBuffer(char* buffer)
{
	for(int i=0;i<sizeof(recvbuf);++i)
		buffer[i] = '\0';
}